package com.simple.common.message.service;

import com.alibaba.fastjson2.JSON;
import com.simple.common.constant.CommonConstants;
import com.simple.framework.core.domain.BaseUser;
import com.simple.framework.core.domain.LoginUser;
import com.simple.framework.core.redis.RedisService;
import com.simple.framework.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description WebSocket服务
 * @Author xiaoke
 * @Date 2024/8/30 9:54
 * @Version 1.0
 */
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{token}")
public class WebSocketService {

    /**
     * 消息实体
     */
    @Data
    public static class WsContent {
        /* 消息类型 */
        private String type;
        /* 消息内容 */
        private String content;
        /* 消息目标Id */
        private Long toUserId;
    }

    /**
     * session集合
     */
    private static ConcurrentHashMap<Long, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 用户集合
     */
    private static ConcurrentHashMap<Long, BaseUser> userMap = new ConcurrentHashMap<>();


    private static RedisService redisService;


    @Autowired
    public void setAppUserService(RedisService r) {
        redisService = r;
    }


    /**
     * 获取用户集合
     *
     * @return 用户集合
     */
    public ConcurrentHashMap<Long, BaseUser> getUser() {
        return userMap;
    }

    /**
     * 移除用户
     *
     * @param userId 用户id
     * @return boolean
     */
    public boolean exit(Long userId) {
        Session session = sessionMap.get(userId);
        if (session == null) {
            return false;
        }
        removeMap(session);
        return true;
    }

    /**
     * 建立连接
     *
     * @param session session
     * @param token   token
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config, @PathParam(value = "token") String token) {
        BaseUser user = parsingToken(token);
        if (user == null) {
            log.info("token【{}】不存在", token);
            try {
                // token无效
                session.getBasicRemote().sendText("TOKEN_INVALID");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                session.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return;
        }
        setMap(session, user);
        log.info("连接成功，token；【{}】", token);
    }


    private BaseUser parsingToken(String token) {
        String userId;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userId = claims.getSubject();
        } catch (Exception e) {
            return null;
        }
        LoginUser loginUser = redisService.getCacheObject(CommonConstants.AUTH_USER + userId);
        return loginUser.getUser();
    }


    /**
     * 关闭连接
     *
     * @param session session
     */
    @OnClose
    public void onClose(Session session) {
        log.info("退出成功");
        removeMap(session);
    }


    /**
     * 验证token
     *
     * @param session session
     */
    private String checkToken(Session session) {
        try {
            return JwtUtil.parseJWT(session.getPathParameters().get("token")).getSubject();
        } catch (Exception e) {
            removeMap(session);
            try {
                session.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
        throw new RuntimeException("token无效");
    }

    /**
     * 接收消息
     *
     * @param session session
     * @param message 消息
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        Long userId = Long.valueOf(checkToken(session));
        if (message == null) {
            return;
        }
        BaseUser user = getUserById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        // 心跳
        if (message.equals("ping")) {
            return;
        }
        log.info("接收到消息：【{}】", message);
        WebSocketService.WsContent msg = JSON.parseObject(message, WebSocketService.WsContent.class);
        sendInfo(msg.getContent(), msg.getToUserId());
    }


    /**
     * 出现错误
     *
     * @param session session
     * @param error   错误
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.info("发生错误");
        error.printStackTrace();
    }


    /**
     * 获取用户
     *
     * @param session session
     * @return 用户id
     */
    private BaseUser getUserBySession(Session session) {
        Long userId = getUserIdBySession(session);
        if (userId == null) {
            return null;
        }
        return userMap.get(userId);
    }

    /**
     * 获取用户
     *
     * @param userId 用户id
     * @return BaseUser
     */
    private BaseUser getUserById(Long userId) {
        return userMap.get(userId);
    }


    /**
     * 发送自定义消息
     *
     * @param message  消息
     * @param toUserId 用户id
     */
    public static void sendInfo(String message, Long toUserId) {
        if (!sessionMap.containsKey(toUserId)) {
            log.info("用户【{}】不在线", toUserId);
            return;
        }
        setMessage(sessionMap.get(toUserId), toUserId, message);
    }

    /**
     * 发送消息
     *
     * @param session 会话
     * @param userId  用户id
     * @param message 消息
     */
    public static void setMessage(Session session, Long userId, String message) {
        log.info("发送消息给用户：{}，消息：{}", userId, message);
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("发送消息出现异常", e);
        }
    }

    /**
     * 设置 session和 user
     *
     * @param session session
     * @param user    用户
     */
    private void setMap(Session session, BaseUser user) {
        sessionMap.put(user.getId(), session);
        userMap.put(user.getId(), user);
        log.info("用户连接：{}，昵称：{}当前在线人数：{}", user.getId(), user.getName(), sessionMap.size());
    }

    /**
     * 移除 session
     *
     * @param session session
     */
    private void removeMap(Session session) {
        Long userId = getUserIdBySession(session);
        if (userId == null) {
            return;
        }
        sessionMap.remove(userId);
        userMap.remove(userId);
        try {
            session.close();
        } catch (IOException e) {
            log.error("关闭连接失败", e);
            throw new RuntimeException(e);
        }
        log.info("当前在线人数：{}", sessionMap.size());
    }


    /**
     * 广播消息
     * @param message 消息
     */
    public void broadcast(String message){
        for (Long useId : sessionMap.keySet()) {
            setMessage(sessionMap.get(useId), useId, message);
        }
    }

    /**
     * 广播消息，排除指定用户
     * @param message 消息
     * @param ids 用户id
     */
    public void broadcastExcludedUserIds(String message, Set<Long> ids) {
        for (Long useId : sessionMap.keySet()) {
            if (!ids.contains(useId)) {
                setMessage(sessionMap.get(useId), useId, message);
            }
        }
    }

    /**
     * 指定用户
     * @param ids 用户id
     * @param message 消息
     */
    public void specifiedUser(Set<Long> ids, String message) {
        for (Long id : ids) {
            Session session = sessionMap.get(id);
            if(session == null) continue;
            setMessage(sessionMap.get(id), id, message);
        }
    }

    /**
     * 获取用户id
     *
     * @param session session
     * @return 用户id
     */
    private Long getUserIdBySession(Session session) {
        for (Long useId : sessionMap.keySet()) {
            if (sessionMap.get(useId).equals(session)) {
                return useId;
            }
        }
        return null;
    }
}
